/* 算法实现模板 */
import AlgorithmBase from "./AlgorithmBase";
import {toKatexString,toVector} from "../../../common/Vector";
import 'katex/dist/katex.min.css';
import { showMathStringInTable } from "../../../visual/Table2D";
import NumpyJS from 'jsnumpy';
import katex from 'katex';
import { txtToMatrix, mdRender} from "../../../common/Common";




class IIS extends AlgorithmBase{
  constructor(){
    super('IIS');
    this.descriptionUrl = `${this.homepage}/data/slm/ch6/6.1/readme.md`;
    this.datasetUrl = `${this.homepage}/data/slm/ch6/6.1/dataset.json`;
  }
  init(){ 
    this.reset();
    this.initTrainDS();
  }
  
  reset(){
    this.dataset = undefined;
    this.trainDS = undefined;
    this.clear();
    this.initDescription();
    this.updateModel();
  }
  
  loadModel(json){
    if(json.TrainDS){
      this.trainDSList = [];
      if(json.TrainDS instanceof Array){
        for(let i = 0, il = json.TrainDS.length; i < il; i++){
          const newTrain = json.TrainDS[i];
          newTrain.T = toVector(newTrain.T, true);
          this.trainDSList.push(newTrain);
        }
        this.trainDS = this.trainDSList[0];
      }
      else{
        this.trainDS = json.TrainDS.x;
        this.label = json.TrainDS.y;
      }
    }

    this._samples = [];
    this._numXY = {};
    this._numXY_ = [];
    
    for(var j=0;j<this.trainDS.length;j++){
      this._samples.push([this.label[j],this.trainDS[j][0],this.trainDS[j][1],this.trainDS[j][2],this.trainDS[j][3]]);
      for(var k=0;k<this.trainDS[j].length;k++){
        this._numXY_.push([this.trainDS[j][k],this.label[j]]);
      }
    }
    
    var count = this.element_count_fun(this._numXY_);
    var set_numXY_ = this.set_two_dimensional_fun(this._numXY_);


    for(var m=0;m<set_numXY_.length;m++){
      this._numXY[set_numXY_[m]] = count[m];
    }
    
    this._Y = this.set_fun(this.label);
    this._N = this.trainDS.length;
    this._ep_ = [];
    this._xyID = {};
    this._n = Object.keys(this._numXY).length;
    this._C = this._samples[0].length-1;
    this._IDxy = {};
    this._w = [];
    this._EPS = 0.005;
    this.lastw = [];   
    var inputTable = [];
    var dataset = this.trainDS;
    this.dataset = dataset;
    var label = this.label;
    inputTable.push([String.raw`\textbf{ID}`,String.raw`\textbf{特征函数}`,String.raw`\textbf{特征函数的分类}`]);
    for(var i =0,il=dataset.length;i<il;i++){
      inputTable.push([]);
      inputTable[i+1][0] = String.raw`${i+1}`;
      inputTable[i+1][1] = String.raw`(${dataset[i]})`;
      inputTable[i+1][2] = String.raw`${label[i]}`;
    }
    if(this.inputDiv) this.inputDiv.innerHTML = '';
    showMathStringInTable(this.inputDiv, inputTable);
    
  }

  initDescription(){
    fetch(this.descriptionUrl).then((response)=>{
      return response.text();
    }).then((txt)=>{

      this.reandMD(txt, this.descriptionDiv)
    })
  }
  
  initTrainDS(){
    this.prepare(this.datasetUrl);
  }


  // 准备数据训练数据
  prepare(url){
    fetch(url).then((response)=>{
      return response.json();
    }).then((json)=>{
      this.trainDS = this.loadModel(json);
      // this.updateModel();
    })
  }

  openFileSelector(){
    this.fileSelector.click();
  }

  // 获取文件路径加载
  handleFileChange(event){
    const files = event.target.files;

    for(let i = 0, il = files.length; i < il; i++){
      let reader = new FileReader();
      reader.onload = (e) => {      
        this.prepare(e.target.result);
        if(i === files.length - 1){
          event.target.value = '';
        }
      };
      reader.readAsDataURL(files[i])
    }
  }

   // 输入数据UI
   initInputUI(){

    // 文件选择器
    const fileSelector      = document.createElement('input');
    fileSelector.type       = 'file';
    fileSelector.hidden     = true;
    fileSelector.onchange   = this.handleFileChange.bind(this);   
    
    this.addButton(this.domElementList.input, "获取服务器数据", this.initTrainDS.bind(this));
    this.addButton(this.domElementList.input, "使用本地数据", this.openFileSelector.bind(this));
    this.domElementList.input.appendChild(fileSelector);
    this.addButton(this.domElementList.input, "训练", this.train.bind(this));
    this.labelTrainTips = this.addLabel(this.domElementList.input, '');
    this.inputDiv       = this.addDiv(this.domElementList.input);
    this.fileSelector  = fileSelector;
  }


  // 输出UI
  initOutputUI(){
    this.outputDiv   = this.addDiv(this.domElementList.output);
    this.trainLogDiv = this.addDiv(this.domElementList.output);
    
  }
  
  set_fun(matrix){
    var set_matrix = [];
    for(var i=0,il=matrix.length;i<il;i++){
      if(set_matrix.indexOf(matrix[i])===-1){
        set_matrix.push(matrix[i]);
      }
    }
    return set_matrix;
  }//返回无重复元素的列表

  set_two_dimensional_fun(array){
    var Array = [];
    for(var i=0;i<array.length;i++){
      Array.push(array[i].toString())
    }
    return this.set_fun(Array);
  }

  element_count_fun(array){
    var Array = [];
    var Array_ = this.set_two_dimensional_fun(array)
    var count = [];
    for(var c=0;c<Array_.length;c++){
      count.push(0);
    }
    for(var i=0;i<array.length;i++){
      Array.push(array[i].toString());
    }
    for(var j=0;j<Array_.length;j++){
      for(var m=0;m<Array.length-1;m++){
        if(Array_[j]===Array[m]){
          count[j] = count[j]+1;
        }
      }
    }
    return count;
  }

  _sample_ep(){
    for(var i=0;i<this._n;i++){
      this._ep_.push(0);
      this._w.push(0);
    }
    this.lastw = this._w;
    var count = 0;
    for(var key in this._numXY){
      this._ep_[count] = this._numXY[key]/this._N;
      this._xyID[key] = count;
      this._IDxy[count] = key;
      count = count+1;
    }
  }

  
  _zx(X){
    var zx = 0;
    var list = [];
    for(var i=0;i<this._Y.length;i++){
      var ss = 0;
      for(var j=0;j<X.length;j++){
        list = [X[j],this._Y[i]];
        if(list in this._numXY){
          ss += this._w[this._xyID[list]];
        }
      }
      zx += Math.exp(ss)
    }
    return zx
  }

  _model_pyx(y,X){
    var Z = this._zx(X);
    var ss = 0;
    var list = [];
    for(var i=0;i<X.length;i++){
      list = [X[i],y];
      if(list in this._numXY){
        ss += this._w[this._xyID[list]];
      }
    }
    var pyx = Math.exp(ss)/Z;
    return pyx
  }

  _model_ep(index){
    var list = this._IDxy[index];
    list = list.split(',')
    var x = list[0];
    var y = list[1];
    var ep = 0;
    for(var i=0;i<this._samples.length;i++){
      if(this._samples[i].indexOf(x) === -1){
        continue;
      }
      var pyx = this._model_pyx(y,this._samples[i]);
      ep += pyx/this._N;
    }
    return ep;
  }

  _convergence(){

  }

  predict(X){
    var z = this._zx(X);
    var result = {};
    var list = [];
    for(var i=0;i<this._Y.length;i++){
      var ss = 0;
      for(var j=0;j<X.length;j++){
        list = [X[j],this._Y[i]];
        if(list in this._numXY){
          ss += this._w[this._xyID[list]];
        }
      }
      var pyx = Math.exp(ss)/z;
      result[this._Y[i]] = pyx;
    }
    console.log(result)
    var string = String.raw`no:${result['no']},yes:${result['yes']}`;
    // mdRender(`${string}`, this.outputDiv);
    
    return result;
  }

  train_data(maxiter = 1000){
    var table = [];
    for(var i=0;i<maxiter;i++){
      this.lastw = this._w;
      for(var j=0;j<this._n;j++){
        var ep = this._model_ep(j);
        this._w[j] += (Math.log(this._ep_[j]/ep))/this._C;
      }
      var w  = [...this._w];
      table.push(w)

    }
    var outputtable = [];
    var count = 1;
    outputtable.push([String.raw`\textbf{循环次数}`,String.raw`\textbf{权重}`]);
    for(var k=0;k<table.length;k=k+50){
      outputtable.push([]);
      outputtable[count][0] = String.raw`${k}`;
      for(var m =0;m<table[k].length;m++){
        table[k][m] = table[k][m].toFixed(3)
      }
      outputtable[count][1] = String.raw`${table[k]}`;
      count+=1;
    }
    // showMathStringInTable(this.outputDiv, outputtable);
  }



  train(){
    this._sample_ep();
    var X = this.dataset;
    var result = [];
    var table = [];
    table.push([String.raw`\textbf{id}`,String.raw`\textbf{数据}`,String.raw`\textbf{yes}`,String.raw`\textbf{no}`])
    this.train_data();
    for(var i=0;i<this.dataset.length;i++){
      result.push(this.predict(this.dataset[i]));
    }
    for (var j=0;j<result.length;j++){
      table.push([]);
      table[j+1][0] = String.raw`${j}`;
      table[j+1][1] = String.raw`${X[j]}`;
      table[j+1][2] = String.raw`${result[j]['yes']}`;
      table[j+1][3] = String.raw`${result[j]['no']}`;

    }
    showMathStringInTable(this.outputDiv,table)
    
    console.log(table)
  }
  
  updateModel(){

  }

}



export default IIS;